package com.ltcode.auth.infrastructure.repository.impl;

import cn.hutool.core.collection.ListUtil;
import com.ltcode.auth.domain.model.LoginSession;
import com.ltcode.auth.domain.repository.LoginSessionRepository;
import com.ltcode.cache.service.RedisService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 登录会话仓储实现
 * 
 * @author ltcode
 * @since 2024-01-01
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class LoginSessionRepositoryImpl implements LoginSessionRepository {

    private final RedisService redisService;

    private static final String SESSION_KEY_PREFIX = "auth:session:";
    private static final String USER_SESSION_KEY_PREFIX = "auth:user:sessions:";
    private static final String TOKEN_SESSION_KEY_PREFIX = "auth:token:session:";
    private static final long DEFAULT_EXPIRE_MINUTES = 120; // 默认2小时过期

    @Override
    public LoginSession save(LoginSession session) {
        try {
            String sessionKey = getSessionKey(session.getSessionId());
            String userSessionKey = getUserSessionKey(session.getUserId());
            String tokenSessionKey = getTokenSessionKey(session.getAccessToken().getTokenId());
            
            // 保存会话信息
            redisService.setCacheObject(sessionKey, session, DEFAULT_EXPIRE_MINUTES, TimeUnit.MINUTES);
            
            // 将会话ID添加到用户会话集合中
//            redisService.setCacheSet(userSessionKey, session.getSessionId());
            redisService.expire(userSessionKey, DEFAULT_EXPIRE_MINUTES, TimeUnit.MINUTES);
            
            // 建立令牌到会话的映射
            redisService.setCacheObject(tokenSessionKey, session.getSessionId(), DEFAULT_EXPIRE_MINUTES, TimeUnit.MINUTES);
            
            log.debug("保存登录会话成功: sessionId={}, userId={}", session.getSessionId(), session.getUserId());
            return session;
        } catch (Exception e) {
            log.error("保存登录会话失败: sessionId={}", session.getSessionId(), e);
            throw new RuntimeException("保存登录会话失败", e);
        }
    }

    @Override
    public Optional<LoginSession> findBySessionId(String sessionId) {
        try {
            String sessionKey = getSessionKey(sessionId);
            LoginSession session = redisService.getCacheObject(sessionKey);
            return Optional.ofNullable(session);
        } catch (Exception e) {
            log.error("查询登录会话失败: sessionId={}", sessionId, e);
            return Optional.empty();
        }
    }

    @Override
    public List<LoginSession> findActiveSessionsByUserId(Long userId) {
        try {
            String userSessionKey = getUserSessionKey(userId);
            Set<String> sessionIds = redisService.getCacheSet(userSessionKey);
            
            return sessionIds.stream()
                    .map(this::findBySessionId)
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .filter(session -> LoginSession.SessionStatus.ACTIVE.equals(session.getStatus()))
                    .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("查询用户活跃会话失败: userId={}", userId, e);
            return ListUtil.empty();
        }
    }

    @Override
    public Optional<LoginSession> findByAccessToken(String accessToken) {
        try {
            String tokenSessionKey = getTokenSessionKey(accessToken);
            String sessionId = redisService.getCacheObject(tokenSessionKey);
            if (sessionId != null) {
                return findBySessionId(sessionId);
            }
            return Optional.empty();
        } catch (Exception e) {
            log.error("根据访问令牌查询会话失败: accessToken={}", accessToken, e);
            return Optional.empty();
        }
    }

    @Override
    public void deleteBySessionId(String sessionId) {
        try {
            Optional<LoginSession> sessionOpt = findBySessionId(sessionId);
            if (sessionOpt.isPresent()) {
                LoginSession session = sessionOpt.get();
                String sessionKey = getSessionKey(sessionId);
                String userSessionKey = getUserSessionKey(session.getUserId());
                String tokenSessionKey = getTokenSessionKey(session.getAccessToken().getTokenId());
                
                // 删除会话
                redisService.deleteObject(sessionKey);
                
                // 从用户会话集合中移除
//                redisService.delCacheSetObject(userSessionKey, sessionId);
                
                // 删除令牌到会话的映射
                redisService.deleteObject(tokenSessionKey);
                
                log.debug("删除登录会话成功: sessionId={}", sessionId);
            }
        } catch (Exception e) {
            log.error("删除登录会话失败: sessionId={}", sessionId, e);
        }
    }

    @Override
    public void updateSessionStatus(String sessionId, LoginSession.SessionStatus status) {
        try {
            Optional<LoginSession> sessionOpt = findBySessionId(sessionId);
            if (sessionOpt.isPresent()) {
                LoginSession session = sessionOpt.get();
                session.setStatus(status);
                save(session);
                log.debug("更新会话状态成功: sessionId={}, status={}", sessionId, status);
            }
        } catch (Exception e) {
            log.error("更新会话状态失败: sessionId={}, status={}", sessionId, status, e);
        }
    }

    @Override
    public void updateLastAccessTime(String sessionId) {
        try {
            Optional<LoginSession> sessionOpt = findBySessionId(sessionId);
            if (sessionOpt.isPresent()) {
                LoginSession session = sessionOpt.get();
                session.updateLastAccessTime();
                save(session);
                log.debug("更新最后访问时间成功: sessionId={}", sessionId);
            }
        } catch (Exception e) {
            log.error("更新最后访问时间失败: sessionId={}", sessionId, e);
        }
    }

    @Override
    public void kickAllSessionsByUserId(Long userId) {
        try {
            List<LoginSession> sessions = findActiveSessionsByUserId(userId);
            for (LoginSession session : sessions) {
                session.kick();
                save(session);
            }
            log.debug("踢出用户所有会话成功: userId={}", userId);
        } catch (Exception e) {
            log.error("踢出用户所有会话失败: userId={}", userId, e);
        }
    }

    @Override
    public void kickOtherSessionsByUserId(Long userId, String currentSessionId) {
        try {
            List<LoginSession> sessions = findActiveSessionsByUserId(userId);
            for (LoginSession session : sessions) {
                if (!session.getSessionId().equals(currentSessionId)) {
                    session.kick();
                    save(session);
                }
            }
            log.debug("踢出用户其他会话成功: userId={}, currentSessionId={}", userId, currentSessionId);
        } catch (Exception e) {
            log.error("踢出用户其他会话失败: userId={}, currentSessionId={}", userId, currentSessionId, e);
        }
    }

    @Override
    public void cleanExpiredSessions() {
        try {
            // Redis的过期机制会自动清理过期的会话
            log.debug("清理过期会话完成");
        } catch (Exception e) {
            log.error("清理过期会话失败", e);
        }
    }

    @Override
    public boolean existsBySessionId(String sessionId) {
        try {
            String sessionKey = getSessionKey(sessionId);
            return redisService.hasKey(sessionKey);
        } catch (Exception e) {
            log.error("检查会话是否存在失败: sessionId={}", sessionId, e);
            return false;
        }
    }

    @Override
    public long countActiveSessionsByUserId(Long userId) {
        try {
            return findActiveSessionsByUserId(userId).size();
        } catch (Exception e) {
            log.error("统计用户活跃会话数量失败: userId={}", userId, e);
            return 0;
        }
    }

    @Override
    public List<LoginSession> findSessionsByLoginIp(String loginIp) {
        try {
            // 这里可以实现根据IP查询会话的逻辑，暂时返回空列表
            return ListUtil.empty();
        } catch (Exception e) {
            log.error("根据IP查询会话失败: loginIp={}", loginIp, e);
            return ListUtil.empty();
        }
    }

    @Override
    public void setSessionExpireTime(String sessionId, long expireMinutes) {
        try {
            String sessionKey = getSessionKey(sessionId);
            redisService.expire(sessionKey, expireMinutes, TimeUnit.MINUTES);
            log.debug("设置会话过期时间成功: sessionId={}, expireMinutes={}", sessionId, expireMinutes);
        } catch (Exception e) {
            log.error("设置会话过期时间失败: sessionId={}, expireMinutes={}", sessionId, expireMinutes, e);
        }
    }

    private String getSessionKey(String sessionId) {
        return SESSION_KEY_PREFIX + sessionId;
    }

    private String getUserSessionKey(Long userId) {
        return USER_SESSION_KEY_PREFIX + userId;
    }

    private String getTokenSessionKey(String tokenId) {
        return TOKEN_SESSION_KEY_PREFIX + tokenId;
    }
}
